﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Cloo;
using RayTracer;
using Processing;
using System.Threading;

namespace GPURayTracer
{
    /// <summary>
    /// Fenster zum treffen der Einstellungen.
    /// </summary>
    public partial class SettingsForm : Form
    {
        #region Attribute
        private ComputeDevice device;
        private ComputePlatform platform;
        private Size maxWindowResolution;
        #endregion Attribute

        #region Eigenschaften
        /// <summary>
        /// Gibt die maximale Fenstergröße für den Raytracer an.
        /// </summary>
        public Size MaxWindowResolution { get { return maxWindowResolution; } }
        #endregion Eigenschaften

        #region Methoden
        /// <summary>
        /// Instanziiert und initialisiert die Klasse.
        /// </summary>
        public SettingsForm()
        {
            InitializeComponent();

            // Anzeigen der verfügbaren Plattformen
            object[] availablePlatforms = new object[ComputePlatform.Platforms.Count];
            for (int i = 0; i < availablePlatforms.Length; i++)
                availablePlatforms[i] = ComputePlatform.Platforms[i].Name;
            cbPlatform.Items.AddRange(availablePlatforms);

            // Falls ein Prozessor existiert wird er wieder angezeigt.
            cbPlatform.SelectedIndex = Program.RayTracer != null ? ComputePlatform.Platforms.IndexOf(Program.RayTracer.Processor.Platform) : 0;

            // Maximale Tiefen auflisten.
            List<object> depths = new List<object>();
            for (int d = 1; d < 9; d++) depths.Add(d.ToString());
            cbRDepth.Items.AddRange(depths.ToArray());
            cbRDepth.SelectedIndex = Program.RayTracer != null ? Program.RayTracer.MaxDepth - 1 : 2;
        }

        /// <summary>
        /// Setzt die Plattform sobald eine ausgewählt wurde.
        /// </summary>
        /// <param name="sender">Der Sender.</param>
        /// <param name="e">Die Ereignisargumente.</param>
        private void cbPlatform_SelectedIndexChanged(object sender, EventArgs e)
        {
            device = null;
            platform = ComputePlatform.Platforms[cbPlatform.SelectedIndex];
            checkDebugmodeIfRequired();

            // Anzeigen der verfügbaren Geräte
            object[] availableDevices = new object[platform.Devices.Count];
            for (int i = 0; i < availableDevices.Length; i++)
                availableDevices[i] = platform.Devices[i].Name;
            cbDevice.Items.Clear();
            cbDevice.Items.AddRange(availableDevices);
            cbDevice.SelectedIndex = 0;

            // Falls ein Prozessor existiert wird er wieder angezeigt.
            if (Program.RayTracer != null)
            {
                for (int i = 0; i < availableDevices.Length; i++)
                {
                    if (platform.Devices[i] == Program.RayTracer.Processor.Device) cbDevice.SelectedIndex = i;
                }
            }
        }

        /// <summary>
        /// Verbirgt das Formular.
        /// </summary>
        /// <param name="sender">Der Sender.</param>
        /// <param name="e">Die Ereignisargumente.</param>
        private void btnAbort_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Übernimmt die Einstellungen und verbirgt das Formular.
        /// </summary>
        /// <param name="sender">Der Sender.</param>
        /// <param name="e">Die Ereignisargumente.</param>
        private void btnOk_Click(object sender, EventArgs e)
        {
            saveSettings();
        }

        /// <summary>
        /// Speichert die Einstellungen und wendet sie an.
        /// </summary>
        private void saveSettings()
        {
            // ToDo: Speicherung noch nicht implementiert!!!

            try
            {
                device = platform.Devices[cbDevice.SelectedIndex];
                try
                {
                    try
                    {
                        if (Program.RayTracer != null)
                        {
                            Program.RayTracer.Stop();
                            Program.RayTracer.Dispose();
                        }
                        Program.RayTracer = new RayTracer.RayTracer(new Processor(platform, device), new Size(32, 32), cbRDepth.SelectedIndex + 1);
                        if (cbDebugMode.Checked) Program.RayTracer.Processor.DebugMode = true;
                        maxWindowResolution = Program.GetGuessedMaxResolution(platform.Devices[cbDevice.SelectedIndex], cbRDepth.SelectedIndex + 1);
                        this.DialogResult = System.Windows.Forms.DialogResult.OK;
                        this.Close();
                    }
                    catch (Exception e) { General.ConsolePrinter.PrintExceptionStack(e); }
                }
                catch { MessageBox.Show(this, "Inkorrekte Auflösung angegeben!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error); }
            }
            catch { MessageBox.Show(this, "Es muss mindestens ein Gerät ausgewählt sein!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error); }
        }

        /// <summary>
        /// Prüft ob eine ATi Plattform gewählt ist und aktiviert entsprechend den Debugmodus.
        /// </summary>
        private void checkDebugmodeIfRequired()
        {
            string platname = "";
            if (platform != null) platname = platform.Name.ToLower();
            cbDebugMode.Checked = (Program.RayTracer != null && Program.RayTracer.Processor != null && Program.RayTracer.Processor.DebugMode) ||
                (Program.RayTracer == null && (platname.IndexOf("ati") >= 0 || platname.IndexOf("amd") >= 0 || platname.IndexOf("stream") >= 0));
        }

        /// <summary>
        /// Aktualisiert die Anzeige des minimalen Speicherbedarfs.
        /// </summary>
        /// <param name="sender">Der Sender.</param>
        /// <param name="e">Die Ereignisargumente.</param>
        private void cbRDepth_SelectedIndexChanged(object sender, EventArgs e)
        {
            Size maxRes = Program.GetGuessedMaxResolution(platform.Devices[cbDevice.SelectedIndex], cbRDepth.SelectedIndex + 1);
            lblMinMemory.Text = "(max. Auflösung " + maxRes.Width + "x" + maxRes.Height + ")";
        }
        #endregion Methoden
    }
}
